package com.mapforge.gui.map;

import java.awt.Component;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;

import com.mapforge.ActionCommands;
import com.mapforge.InputMapping;
import com.mapforge.gui.JPopupMenuModifier;
import com.mapforge.gui.JPopupMenuProvider;
import com.mapforge.gui.interfaces.CutCopyPasteListenerInterface;
import com.mapforge.gui.interfaces.TileMapEditorGUIInterface;
import com.mapforge.gui.tools.SelectedTiles;
import com.mapforge.listener.TileLayerDeletionEvent;
import com.mapforge.listener.TileLayerDeletionListener;
import com.mapforge.listener.TileLayerSelectionEvent;
import com.mapforge.listener.TileLayerSelectionListener;
import com.mapforge.listener.TileMapCreationEvent;
import com.mapforge.listener.TileMapCreationListener;
import com.mapforge.map.TileLayer;
import com.mapforge.map.TileMap;

/**
 * The {@link TileMapEditorGUIPopupMenuProvider} class provides
 * {@link JMenuPopup popup's} for {@link TileMapEditorGUIInterface} components.
 * 
 * @see JPopupMenuProvider
 * @author Will Morrison
 * 
 */
public class TileMapEditorGUIPopupMenuProvider implements
		JPopupMenuProvider<TileMapEditorGUIInterface>, TileMapCreationListener,
		TileLayerSelectionListener, TileLayerDeletionListener {
	private JMenuItem itmCut;
	private JMenuItem itmCopy;
	private JMenuItem itmPaste;
	private JMenuItem itmCreateTilestamp;
	private JMenuItem itmUndo;
	private JMenuItem itmRedo;
	private TileLayer tileLayer;
	private JPopupMenu popupMenu;
	private TileMapEditorGUIInterface tileMapEditorGUI;
	private List<JPopupMenuModifier> popupMenuModifierList;

	private CutCopyPasteListenerInterface listener;

	public TileMapEditorGUIPopupMenuProvider(
			TileMapEditorGUIInterface tileMapEditorGUI,
			CutCopyPasteListenerInterface listener) {
		this.popupMenuModifierList = new ArrayList<JPopupMenuModifier>();
		this.tileMapEditorGUI = tileMapEditorGUI;
		Component c = (Component) tileMapEditorGUI;

		// TODO DON'T add mouse listener in this class.
		MouseAdapter mouselistener = new TileMapPanelMouseListener();
		c.addMouseListener(mouselistener);
		c.addMouseMotionListener(mouselistener);

		this.listener = listener;
		this.itmCut = createMenuItem("Cut");
		this.itmCut.setActionCommand(ActionCommands.CUT_TILES_CMD);
		this.itmCopy = createMenuItem("Copy");
		this.itmCopy.setActionCommand(ActionCommands.COPY_TILES_CMD);
		this.itmPaste = createMenuItem("Paste");
		this.itmPaste
				.setActionCommand(ActionCommands.PASTE_TILES_AT_LOCATION_CMD);
		this.itmUndo = createMenuItem("Undo");
		this.itmRedo = createMenuItem("Redo");
		this.itmCreateTilestamp = new JMenuItem("Create Tilestamp");
		this.itmCreateTilestamp
				.setActionCommand(ActionCommands.CREATE_TILESTAMP_CMD);

		popupMenu = new JPopupMenu();
	}

	public JMenuItem getCreateTileStampItem() {
		return this.itmCreateTilestamp;
	}

	public JMenuItem createMenuItem(String txt) {
		JMenuItem item = new JMenuItem(txt);
		item.addActionListener(listener);
		return item;
	}

	public boolean addJPopupMenuModifier(JPopupMenuModifier modifier) {
		return popupMenuModifierList.add(modifier);
	}

	public boolean removeJPopupMenuModifier(JPopupMenuModifier modifier) {
		return popupMenuModifierList.remove(modifier);
	}

	/**
	 * Handles initializing the {@link JPopupMenu} offering actions the user can
	 * perform on a {@link TileMap TileMap.} Registered
	 * {@link JPopupMenuProvider JPopupMenuProviders} will be called to modify
	 * the {@link JPopupMenu} before returning the final constructed menu.
	 * 
	 * @param event
	 *            {@link MouseEvent} which initiated the construction of this
	 *            {@link JPopupMenu}
	 * @return a {@link JPopupMenu} with actions for a {@link TileMap}
	 */
	public JPopupMenu showJPopupMenu(MouseEvent event) {
		SelectedTiles selectedTiles = SelectedTiles.getHandle();
		if (selectedTiles.isEmpty() || tileLayer == null) {
			this.itmPaste.setEnabled(false);
			this.itmCut.setEnabled(false);
			this.itmCopy.setEnabled(false);
			this.itmCreateTilestamp.setEnabled(false);
		} else {
			this.itmPaste.setEnabled(true);
			this.itmCut.setEnabled(true);
			this.itmCopy.setEnabled(true);
			this.itmCreateTilestamp.setEnabled(true);
		}
		System.out.println("TileMapEditorGUIPopUp: " + selectedTiles.isEmpty()
				+ " " + tileLayer);
		popupMenu.removeAll();
		// popupMenu.add(itmUndo);
		// popupMenu.add(itmRedo);
		// popupMenu.addSeparator();
		popupMenu.add(itmPaste);
		popupMenu.add(itmCopy);
		popupMenu.add(itmCut);
		popupMenu.addSeparator();
		popupMenu.add(itmCreateTilestamp);
		int itemCount = popupMenu.getComponentCount();
		for (JPopupMenuModifier modifier : popupMenuModifierList) {
			popupMenu.addSeparator();
			if (!modifier.modifyPopupMenu(popupMenu)) {
				popupMenu.remove(itemCount);
			} else {
				itemCount = popupMenu.getComponentCount();
			}
		}
		if (!popupMenu.isVisible()) {
			Point point = this.tileMapEditorGUI.convertPointToTileCoordinates(
					event.getX(), event.getY());
			if (point != null) {
				this.listener.setPoint(point);
				popupMenu.show((Component) event.getSource(), event.getX(),
						event.getY());
			}

		} else {
			popupMenu.setLocation(event.getX(), event.getY());
		}
		return popupMenu;
	}

	private void constructAndShowPopupMenu(MouseEvent e) {
		if (e.getButton() == InputMapping.POPUP_TRIGGER) {
			this.showJPopupMenu(e);
		}
	}

	@Override
	public void tileMapCreationPerformed(TileMapCreationEvent mapCreationEvent) {
	}

	private class TileMapPanelMouseListener extends MouseAdapter {
		public void mouseReleased(MouseEvent e) {
			constructAndShowPopupMenu(e);
		}
	}

	@Override
	public TileMapEditorGUIInterface getComponent() {
		return this.tileMapEditorGUI;
	}

	@Override
	public void tileLayerSelectionPerformed(TileLayerSelectionEvent event) {
		int num = event.getNumberLayersSelected();
		if (num == 1) {
			this.tileLayer = event.iterator().next();
		} else {
			this.tileLayer = null;
		}
	}

	@Override
	public void tileLayerDeletionPerformed(TileLayerDeletionEvent event) {
		Iterator<TileLayer> it = event.getDeletedLayerIterator();
		while (it.hasNext()) {
			if (tileLayer == it.next()) {
				break;
			}
		}
	}
}